#include "manager.h"
#include "Defines.h"
#include "GameObject.h"
#include "RenderSystem.h"
#include "profiler.h"
#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <math.h>
Pool::Pool()
{
	this->Mask=0;
}

void Pool::Init(Manager* Mgr, unsigned int pid)
{
	SPEED_TEST;
	this->ObjectList.clear();
	this->uiCurrentObject=0;
	this->mgr=Mgr;
	
	this->ID=pid;
	if (this->ID==0)
	{
		PPanzer *pp = new PPanzer(this->mgr->GenID(), GID_USERPANZER, this, SPAWN_P_X+0.5, SPAWN_P_Y+0.5, MOVE_UP, P_TYPE_U_N);
		AIPanzer *ap1= new AIPanzer(this->mgr->GenID(), GID_AIPANZER, this, SPAWN_AI1_X+0.5, SPAWN_AI1_Y+0.5, MOVE_DOWN, P_TYPE_AI_1+rand()%3);
		AIPanzer *ap2= new AIPanzer(this->mgr->GenID(), GID_AIPANZER, this, SPAWN_AI2_X+0.5, SPAWN_AI2_Y+0.5, MOVE_DOWN, P_TYPE_AI_1+rand()%3);
		AIPanzer *ap3= new AIPanzer(this->mgr->GenID(), GID_AIPANZER, this, SPAWN_AI3_X+0.5, SPAWN_AI3_Y+0.5, MOVE_DOWN, P_TYPE_AI_1+rand()%3);                
		ObjectList.push_back(pp);
		ObjectList.push_back(ap1);
		ObjectList.push_back(ap2);
		ObjectList.push_back(ap3);
		uiObjectCount+=4;
	} else
	if (this->ID == 1)
	{
		for (int i =0; i< MAP_SIZE_X; i++)
			for (int j=0; j<MAP_SIZE_Y; j++)
			{
				if (this->mgr->terrain.Map[i][j]&MAP_BRICK)
				{
					Wall *wall = new Wall(this->mgr->GenID(), GID_WALL, this, float(i)+0.5f, float(j)+0.5f, false);
					ObjectList.push_back(wall);
					uiObjectCount++;
				} else 
				if (this->mgr->terrain.Map[i][j]&MAP_CONC)
				{
					Wall *wall = new Wall(this->mgr->GenID(), GID_WALL, this, float(i)+0.5f, float(j)+0.5f, true);
					ObjectList.push_back(wall);
					uiObjectCount++;
				} else
				if (this->mgr->terrain.Map[i][j]&MAP_STAFF)
				{
					Staff *stf = new Staff(this->mgr->GenID(),GID_STAFF, this, float(i)+0.5f, float(j)+0.5f);
					ObjectList.push_back(stf);
					uiObjectCount++;
				}
			}
	}
}
//--------------------------------------------------------
void Pool::SetFilter(unsigned int Mask_)
{
	this->Mask=Mask_;
}
//--------------------------------------------------------
unsigned int Pool::GetID()
{
	return this->ID;
}
Manager::~Manager()
{
	;
}
//--------------------------------------------------------
void Pool::Render()
{
	SPEED_TEST;
	list<GameObject*>::iterator iter=this->ObjectList.begin();
	for(iter=this->ObjectList.begin(); iter!=this->ObjectList.end(); iter++)
		(*iter)->Render();
}
GameObject* Pool::GetObjectByID(unsigned int ID)
{
	SPEED_TEST;
	list<GameObject*>::iterator iter=this->ObjectList.begin();
	for(iter=this->ObjectList.begin(); iter!=this->ObjectList.end(); iter++)
	{
		if ((*iter)->uiID == ID) return *iter;	
	}
	return 0;
}
void Pool::Message(Msg Message)
{
	SPEED_TEST; 
	this->uiCurrentObject=0;
	if (Message.uiEvent&this->Mask)
	{
		if (Message.uiEvent&MSG_MOVE)
		{
			Msg tmp_msg;
			tmp_msg.dx=Message.dx;
			tmp_msg.dy=Message.dy;
			tmp_msg.uiIdPool=ID;
			this->GetObjectByID(Message.uiIdObject)->Message(&tmp_msg);
		} else
		//if (Message.uiEvent&MSG_UPDATE)
		//{
	/////		list<GameObject*>::iterator iter=this->ObjectList.begin();
		//	for(iter=this->ObjectList.begin(); iter!=this->ObjectList.end(); iter++)
	//			(*iter)->Message(&Message);	
		//} else
		if (Message.uiEvent&MSG_CREATE)
		{
			if ((Message.uiObjectType&GID_AIBULLET)||(Message.uiObjectType&GID_USERBULLET))
			{
				Bullet * b = new Bullet(mgr->GenID(), Message.uiObjectType, this, Message.x, Message.y, Message.direct, Message.uiIdObject);
				this->mgr->Pools[2].ObjectList.push_back(b);
				this->mgr->Pools[2].uiObjectCount++;
			} else
			if ((Message.uiObjectType>6)&&(Message.uiObjectType<14))
			{
				Bonus *b = new Bonus(mgr->GenID(), 7+rand()%6, this, Message.x, Message.y);
				b->Rot.Y=Message.direct;
				this->mgr->Pools[1].ObjectList.push_back(b);
				this->mgr->Pools[1].uiObjectCount++;
			}
		} else
		if ((Message.uiEvent == MSG_KB)||(Message.uiEvent == MSG_UPDATE))
		{
			list<GameObject*>::iterator iter=this->ObjectList.begin();
			for(iter=this->ObjectList.begin(); iter!=this->ObjectList.end(); iter++)
				(*iter)->Message(&Message);
		} else
		if (Message.uiEvent == MSG_DEATH)
		{
			list<GameObject*>::iterator iter=this->ObjectList.begin();
			for(iter=this->ObjectList.begin(); iter!=this->ObjectList.end(); iter++)
			{
				if ((*iter)->uiID == Message.uiIdObject)
				{	
					delete (*iter);
					iter =ObjectList.erase(iter);
					uiObjectCount--;
					break;
				}
				
			}
		}
		
	}
}
unsigned int Pool::GetNextObject()
{
	SPEED_TEST;
	if (ObjectList.size()<=this->uiCurrentObject) 
	{
		this->uiCurrentObject =0;
		return 0;
	}
	list<GameObject*>::iterator iter=this->ObjectList.begin();
	for (int i=0; i<this->uiCurrentObject; i++)
		iter++;
	
	uiCurrentObject++;
	return (*iter)->uiID;
	
}

Manager::Manager()
{
	this->maxID=1;

}

void Manager::Init(Application* ap)
{
	SPEED_TEST;
	this->App=ap;
	this->Statistic.mgr=this;
	this->LoadIndexFile("media\\maps\\index.imd");
	this->LoadMap();
	Pool tank_pool;
	tank_pool.Mask=MSG_MOVE|MSG_KB|MSG_DEATH|MSG_CREATE|MSG_COLLISION|MSG_UPDATE;
	Pool land_pool;
	land_pool.Mask=MSG_CHNGLVL|MSG_COLLISION|MSG_DEATH;
	Pool bullet_pool;
	bullet_pool.Mask=MSG_COLLISION|MSG_DEATH|MSG_MOVE|MSG_UPDATE;
	this->Pools.push_back(tank_pool);
	this->Pools.push_back(land_pool);
	this->Pools.push_back(bullet_pool);
	this->Pools[0].Init(this, 0);
	this->Pools[1].Init(this, 1);
	this->Pools[2].Init(this, 2);
}
void Manager::LoadIndexFile(char * filename)
{
	SPEED_TEST;
	char tmpstr[256];
	int count=0;
	FILE * imd;
	imd=fopen(filename, "r+");
	while(!feof(imd))
	{
		fgets(tmpstr,256,imd);
		strcpy(MapIndexFile[count],tmpstr);
		count++;
	}
	this->uiMapCount=count;
	this->uiCurrrentMap=0;
	fclose(imd);

}
void Manager::LoadMap()
{
	SPEED_TEST;
	char filename[256]="media\\maps\\";
	strcat(filename,this->MapIndexFile[this->uiCurrrentMap]);
	int l=strlen(filename);
	filename[l-1]=0;
	byte temp=0;
	char tmpstr[16];
	FILE * map;
	map=fopen(filename, "r+");
	fgets(tmpstr, 16, map);
	sscanf(tmpstr,"%i",&uiAICount);
	fgets(tmpstr, 16, map);
	sscanf(tmpstr,"%i",&uiAIMax);
	for (int i=0; i<MAP_SIZE_X; i++)
	for (int j=0; j<MAP_SIZE_Y; j++)
	{
		fgets(tmpstr,16,map);
		for (int k=0; k<strlen(tmpstr)-1; k++)
		{
			temp+=(tmpstr[k]-'0')*pow(10., int(strlen(tmpstr)-2-k));
		}
		this->terrain.Map[i][j]=temp;
		temp=0;
		strcpy(tmpstr, "");
	}
	fclose(map);
	terrain.Map[SPAWN_P_X][SPAWN_P_Y]=0;
	terrain.Map[SPAWN_AI1_X][SPAWN_AI1_Y]=0;
	terrain.Map[SPAWN_AI2_X][SPAWN_AI2_Y]=0;
	terrain.Map[SPAWN_AI3_X][SPAWN_AI3_Y]=0;
	if (uiCurrrentMap<uiMapCount-1)
		uiCurrrentMap++;
	Statistic.NewLevel(this->uiAICount, 10000000,2000000, uiAIMax);

}
unsigned int Manager::GenID()
{
	this->maxID++;
	return this->maxID-1;
}
void Manager::Message(Msg * M)
{
	Queue.push_back(*M);
}
void Manager::ProcessMessages()
{
	SPEED_TEST;
	bool collision=0;
	Msg tmp_msg;
	unsigned int temp_id;
	GameObject * temp_obj;
	GameObject * send_obj;
	while (!this->Queue.empty())
	{
		tmp_msg = *(this->Queue.begin());
		Queue.pop_front();
		send_obj = this->GetObjectByID(tmp_msg.uiIdObject);
		if (tmp_msg.uiEvent == MSG_MOVE)
		{
			for (int i=0; i<3; i++)
			{
				while(0!=(temp_id = this->Pools[i].GetNextObject()))
				{
					if (temp_id != tmp_msg.uiIdObject)
					{
						temp_obj=this->Pools[i].GetObjectByID(temp_id);
						if (sqrt(pow(tmp_msg.dy-temp_obj->Pos.Y,2)+pow(tmp_msg.dx-temp_obj->Pos.X,2))+1.1 <=send_obj->Size.X+temp_obj->Size.X)
						{
							Msg Out_msg;
							Out_msg.uiEvent=MSG_COLLISION;
							Out_msg.uiIdObject=temp_obj->uiID;
							Out_msg.uiIdPool=i;
							Out_msg.Reason=1;
							Out_msg.Water=0;
							Out_msg.Border=0;
							Out_msg.dx=tmp_msg.dx;
							Out_msg.dy=tmp_msg.dy;
							send_obj->Message(&Out_msg);
							Out_msg.uiIdObject=send_obj->uiID;
							Out_msg.Reason=0;
							temp_obj->Message(&Out_msg);
							collision=1;
							this->Pools[i].uiCurrentObject=0;
							break;
						} 
					}
					if (collision) break;
				}
				if((tmp_msg.dx>MAP_SIZE_X-0.49)||(tmp_msg.dx<0.49)||(tmp_msg.dy>MAP_SIZE_Y-0.49)||(tmp_msg.dy<0.49))
				{
					Msg Out_msg;
					Out_msg.uiEvent=MSG_COLLISION;
					Out_msg.Border=1;
					collision=1;
					send_obj->Message(&Out_msg);
				}
				
			}
			if (!collision)
			{
				if((this->terrain.Map[int(tmp_msg.dx-0.49)][int(tmp_msg.dy-0.49)]!=MAP_WATER)&&
					(this->terrain.Map[int(tmp_msg.dx+0.49)][int(tmp_msg.dy+0.49)]!=MAP_WATER))
				{
					Msg Out_msg;
					Out_msg.uiEvent=MSG_MOVE;
					Out_msg.uiIdPool=tmp_msg.uiIdPool;
					Out_msg.dx=tmp_msg.dx;
					Out_msg.dy=tmp_msg.dy;
					send_obj->Message(&Out_msg);
				} else
				{
					Msg Out_msg;
					Out_msg.uiEvent=MSG_COLLISION;
					Out_msg.uiIdPool=0;
					Out_msg.Border=0;
					Out_msg.Water=1;
					send_obj->Message(&Out_msg);
				}

			}
		} else
		if (tmp_msg.uiEvent == MSG_CREATE)
		{
			this->Pools[0].Message(tmp_msg);
		} else
		if (tmp_msg.uiEvent == MSG_DEATH)
		{
			this->Statistic.Message(&tmp_msg);
			for (int i=0; i<3; i++)
				this->Pools[i].Message(tmp_msg);
		} else
		if (tmp_msg.uiEvent == MSG_BOMB)
		{
			this->Pools[0].Message(tmp_msg);
		} else
		if (tmp_msg.uiEvent == MSG_KB)
		{
			for (int i=0; i<3; i++)
				this->Pools[i].Message(tmp_msg);
		} else
		if (tmp_msg.uiEvent == MSG_CHNGLVL)
		{
			if (tmp_msg.fWin)
				this->ChangeLevel();
			else
			{
				this->uiCurrrentMap--;
				this->ChangeLevel();
			}
		}
	}
}

GameObject* Manager::GetObjectByID(unsigned int ID)
{
	SPEED_TEST;
	GameObject* tmp=0;
	int i=0;
	tmp=this->Pools[0].GetObjectByID(ID);
	if(tmp==0) tmp=this->Pools[1].GetObjectByID(ID);
	if (tmp==0) tmp=this->Pools[2].GetObjectByID(ID);

	return tmp;
}
void Manager::Update(unsigned int t)
{
	SPEED_TEST;
	list<Explosion>::iterator iter=this->ExpList.begin();
	for(iter=this->ExpList.begin(); iter!=this->ExpList.end(); iter++)
	{
		(*iter).Update(t);
	}
	iter=this->ExpList.begin();
	for(iter=this->ExpList.begin(); iter!=this->ExpList.end(); iter++)
	{
		if ((*iter).m_uiLastTime>=EXP_LONG)
		{
			this->ExpList.erase(iter);
			break;
		}
	}


	Msg tmp_msg;
	tmp_msg.uiEvent=MSG_UPDATE;
	tmp_msg.uiTime=t;
	Statistic.Message(&tmp_msg);
	for(int i=0; i<3; i++)
		this->Pools[i].Message(tmp_msg);
}
void Manager::RenderAll()
{
	SPEED_TEST;
	this->terrain.Render();
	for (int i=0; i<3; i++)
	{
		this->Pools[i].Render();
	}

}
Pool::~Pool()
{
	SPEED_TEST;
	this->Clear();
}
void Manager::AddExplosion(float x, float y)
{
	Explosion exp;
	exp.Load("./media/graphics/particle.dds");
	exp.Pos.X=x;
	exp.Pos.Y=y;
	this->ExpList.push_back(exp);
}
                    
void Manager::RenderExplosions()
{
	list<Explosion>::iterator iter=this->ExpList.begin();
	for(iter=this->ExpList.begin(); iter!=this->ExpList.end(); iter++)
	{
		(*iter).Render();
	}
}
void Pool::Clear()
{
	SPEED_TEST;
	list<GameObject*>::iterator iter=this->ObjectList.begin();
	for(iter=this->ObjectList.begin(); iter!=this->ObjectList.end(); iter++)
	{
		delete (*iter);
	}
	this->ObjectList.clear();
}
void Manager::ChangeLevel()
{
	this->LoadMap();
	this->terrain.Load("media\\graphics\\tiles.dds");
	for (int i=0; i<3; i++)
	{
		Pools[i].Clear();
		Pools[i].Init(this,i);
	}
	

}